﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BurningSphere.Simulator.Controls;
using System.Reflection;
using Sanford.Multimedia.Midi;
using System.Diagnostics;
using BurningSphere.Simulator.Code;
using System.IO;
using System.Globalization;

namespace BurningSphere.Simulator.MIDI
{
	/// <summary>
	/// MidiNoteOn(<channel>,[<value>])
	/// MidiNoteOff(<channel>,<value>)
	/// MidiVelocity(<channel>)
	/// MidiController(<channel>,<control index>)
	/// </summary>
	public class MidiProperty
	{
		static string template;
		object instance;
		PropertyInfo property;
		bool abort;

		protected int Channel;
		protected int Command;
		protected int Data1;
		protected int Data2;

		/// <summary>
		/// Ctor
		/// </summary>
		public MidiProperty(
			object instance,
			PropertyInfo property
			)
		{
			this.property = property;
			this.instance = instance;

			if (property.PropertyType != typeof(int) &&
				property.PropertyType != typeof(short) &&
				property.PropertyType != typeof(char) &&
				property.PropertyType != typeof(byte) &&
				property.PropertyType != typeof(ushort) &&
				property.PropertyType != typeof(uint) &&
				property.PropertyType != typeof(float) &&
				property.PropertyType != typeof(double)
				)
			{
				throw new Exception("Property " + property.Name + " needs to be a numeric type to be used with MIDI input");
			}

			Form1.Midi.Device.ChannelMessageReceived += new EventHandler<Sanford.Multimedia.Midi.ChannelMessageEventArgs>(ChannelMessageReceived);
		}


		/// <summary>
		/// Midi message
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void ChannelMessageReceived(object sender, Sanford.Multimedia.Midi.ChannelMessageEventArgs e)
		{
			Channel = e.Message.MidiChannel;
			Data1 = e.Message.Data1;
			Data2 = e.Message.Data2;
			Command = (int)e.Message.Command;

			SetProperty(GetValue());
		}

		/// <summary>
		/// Get
		/// </summary>
		/// <returns></returns>
		protected virtual double GetValue()
		{
			throw new ApplicationException();
		}

		/// <summary>
		/// Set
		/// </summary>
		/// <param name="value"></param>
		protected void SetProperty(double value)
		{
			if (abort)
			{
				abort = false;
				return;
			}
			property.SetValue(instance, Convert.ChangeType(value, property.PropertyType), null);
		}

		/// <summary>
		/// Abort
		/// </summary>
		protected void Abort()
		{
			abort = true;
		}

		public static MidiProperty BuildPropertyFromScript(
			object instance,
			PropertyInfo property,
			string script
			)
		{
			if (property.PropertyType == typeof(string))
			{
				property.SetValue(instance, script, null);
				return null;
			}

			if (template == null)
			{
				template = File.ReadAllText(@"Data\MidiPropertyProxy.cs.txt");
			}

			string name = instance.GetType().Name.Replace(".","_")+"_"+property.Name;
			Script compiler = new Script(template,name);
			compiler.Include.Add("BurningSphere.Simulator.exe");
			compiler.Code = script;
			Assembly asm = compiler.Assembly;
			MidiProperty midiProperty = (MidiProperty)asm.CreateInstance(
				"BurningSphere.Simulator.Data." + name,
				false,
				BindingFlags.CreateInstance,
				null,
				new object[2]{
					instance,
					property
					},
				CultureInfo.CurrentCulture,
				null
			);

			return midiProperty;
		}
	}
}
